﻿using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using MathNet.Numerics;
using MathNet.Numerics.LinearRegression;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Statistics;
using System.Windows.Markup;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.Optimization;
using System.Xml.Linq;
using MathNet.Numerics.LinearAlgebra.Factorization;
using System.Diagnostics;

namespace OxyplotWPFDemo.Controls
{
    /// <summary>
    /// 柱状图.xaml 的交互逻辑
    /// </summary>
    public partial class 柱状图 : UserControl
    {
        private Random rand = new Random(0);
        private double[] RandomWalk(int points = 5, double start = 100, double mult = 50)
        {
            // return an array of difting random numbers
            double[] values = new double[points];
            values[0] = start;
            for (int i = 1; i < points; i++)
                values[i] = values[i - 1] + (rand.NextDouble() - .5) * mult;
            return values;
        }

        public 柱状图()
        {
            InitializeComponent();

        }

        public void ChangePlot(int index)
        {
            switch (index)
            {
                case 1:
                    plotView1.Model = Example1();
                    break;
                case 2:
                    plotView1.Model = Example2();
                    break;
                case 3:
                    plotView1.Model = Example3();
                    break;
                case 4:
                    plotView1.Model = Example4();
                    break;
                case 5:
                    plotView1.Model = Example5();
                    break;
                case 6:
                    plotView1.Model = Example6();
                    break;
                case 7:
                    plotView1.Model = Example7();
                    break;
                case 8:
                    plotView1.Model = Example8();
                    break;
                case 9:
                    plotView1.Model = Example9();
                    break;
                default:
                    plotView1.Model = Example1();
                    break;
            }
        }

        //原始算法
        public PlotModel Example99()
        {
            PlotModel plotModel = null;
            double slope = 0;
            double intercept = 0;
            string formula = "";
            double[] xdata = { 1, 2, 3, 4, 5 };
            double[] ydata = { 2.1, 3.9, 7.0, 11.1, 16.1 };
            try
            {
                double correlation = Math.Round(Correlation.Pearson(xdata, ydata), 4);//线性相关系数，开方后
                correlation = Math.Abs(correlation);//绝对值
                #region ===散点
                var scatterSeries = new ScatterSeries
                {
                    MarkerType = MarkerType.Circle,
                    MarkerSize = 3,
                    MarkerFill = OxyColors.Blue
                };
                #endregion

                #region ===
                var data = new List<DataPoint>();
                for (int i = 0; i < xdata.Length; i++)
                {
                    data.Add(new DataPoint(xdata[i], ydata[i]));
                    scatterSeries.Points.Add(new ScatterPoint(xdata[i], ydata[i]));
                }

                // 计算线性回归参数
                double sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
                int n = data.Count;

                foreach (var point in data)
                {
                    sumX += point.X;
                    sumY += point.Y;
                    sumXY += point.X * point.Y;
                    sumXX += point.X * point.X;
                }

                slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
                intercept = (sumY - slope * sumX) / n;
                var str_b = intercept.ToString("0.0000");
                if (intercept >= 0)
                {
                    str_b = "+" + intercept.ToString("0.0000");
                }
                formula = $"y={Math.Round(slope, 4)}x {str_b} , r={correlation}";
                plotModel = new PlotModel { Title = formula };
                plotModel.Series.Add(scatterSeries);
                var start = Math.Round(xdata.First(), 2);
                var end = Math.Round(xdata.Last(), 2);
                var www = (int)(end * 100 - start * 100) + 2;
                // 添加线性拟合线
                var regressionLine = new LineSeries
                {
                    Color = OxyColors.Red,
                    StrokeThickness = 2,
                    Dashes = new double[] { 2, 2 },
                    ItemsSource = Enumerable.Range((int)(start * 100), (int)(end * 100 - start * 100) + 2).Select(x => new DataPoint((double)x / 100, slope * (double)x / 100 + intercept)).ToList()
                };

                plotModel.Series.Add(regressionLine);


                #endregion
            }
            catch (Exception ex)
            {
                //_logger.Error(ex, ex.Message);
            }
            return plotModel;
        }

        //MathNet.Numerics 库算法
        public PlotModel Example9()
        {
            PlotModel plotModel = null;

            double[] xData = { 1, 2, 3, 4, 5 };
            double[] yData = { 1, 1.2, 3, 7, 7 };

            #region 添加点
            var scatterSeries = new ScatterSeries
            {
                MarkerType = MarkerType.Circle,
                MarkerSize = 3,
                MarkerFill = OxyColors.Blue
            };
            var data = new List<DataPoint>();
            for (int i = 0; i < xData.Length; i++)
            {
                data.Add(new DataPoint(xData[i], yData[i]));
                scatterSeries.Points.Add(new ScatterPoint(xData[i], yData[i]));
            }
            plotModel = new PlotModel { Title = "" };
            plotModel.Series.Add(scatterSeries);

            #endregion

            #region 拟合直线：

            //var regressionLine = new LineSeries
            //{
            //    Color = OxyColors.Red,
            //    StrokeThickness = 2,
            //    Dashes = new double[] { 2, 2 }
            //};
            //plotModel.Series.Add(regressionLine);
            ////s为拟合出来直线参数，s.Item2为斜率k，s.Item1为斜率b
            ////公式为y=s.Item2*x+s.Item1
            //var s = Fit.Line(xData, yData);
            //for (int i = 0; i < xData.Length; i++)
            //{
            //    double y = s.B * xData[i] + s.A;
            //    regressionLine.Points.Add(new DataPoint(xData[i], y));
            //}

            #endregion

            #region 拟合曲线（多项式）：
            ////plotModel.Axes.Add(new LinearAxis() { MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, TickStyle = TickStyle.Outside, Position = AxisPosition.Bottom, Title = "X" });
            ////plotModel.Axes.Add(new LinearAxis() { MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, TickStyle = TickStyle.Outside, Position = AxisPosition.Left, Title = "Y" });
            //var regressionLine = new LineSeries
            //{
            //    Color = OxyColors.Red,
            //    StrokeThickness = 2,
            //    Dashes = new double[] { 2, 2 }
            //};
            //plotModel.Series.Add(regressionLine);

            //List<double> newY = new List<double>();

            ////X为x轴的数组，Y为y轴数组，m为次方数
            ////我们使用较多的4次方，m就输入4，公式为：y=res[4]*x^4+res[3]*x^3+res[2]*x^2+res[1]*x+res[0]
            ////计算曲线拟合出来的R^2  这个值越接近1，说明拟合出来的曲线跟原曲线就越接近  RSquared=GoodnessOfFit.RSquared(Y, Ytest);
            //double[] res = Fit.Polynomial(xData, yData, 4);
            //for (int i = 0; i < xData.Length; i++)
            //{
            //    double y = res[4] * Math.Pow(xData[i], 4) + res[3] * Math.Pow(xData[i], 3) + res[2] * Math.Pow(xData[i], 2) + res[1] * xData[i] + res[0];
            //    //Y=（A-D）/（1+（X/C）＾B） + D
            //    //double y = res[2] * Math.Pow(xData[i], 2) + res[1] * xData[i] + res[0];
            //    regressionLine.Points.Add(new DataPoint(xData[i], y));
            //    newY.Add(y);
            //}
            ////拟合比例  R^2 
            //var result = CalculateRSquared(yData, newY.ToArray());

            #region MyRegion
            //// f(x) = A*x*x*x + B*x*x + C*x+D
            //GaussNewton.F ff = delegate (double[] coefficients, double x)
            //{
            //    return coefficients[0] * x * x * x + coefficients[1] * x * x + coefficients[2] * x + coefficients[3];
            //};

            //GaussNewton gaussNewton1 = new GaussNewton(4);
            //gaussNewton1.Initialize(yData, xData, ff);
            //double[] answer1 = gaussNewton1.Coefficients;     //A=-1 B=2 C=3

            //for (int i = 0; i < xData.Length; i++)
            //{
            //    double xx = xData[i];
            //    double y = answer1[0] * xx * xx * xx + answer1[1] * xx * xx + answer1[2] * xx + answer1[3];
            //    regressionLine.Points.Add(new DataPoint(xData[i], y));
            //}

            #endregion

            #endregion

            #region 四参数模式  拟合曲线
            //四参数模式为   Y=(A - D)/(1 +(X / C)＾B) +D
            //a：检测中的最小值 b：曲线在c处的斜率  c：拐点——曲线的中点 d：检测中的最大值
            //坐标轴
            var _valueAxis = new LinearAxis()
            {
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.Dot,
                IntervalLength = 80,
                Angle = 60,
                IsZoomEnabled = false,
                IsPanEnabled = false,
                Maximum = 10,
                Minimum = -1,
                Title = "Y轴"
            };
            plotModel.Axes.Add(_valueAxis);
            //定义线
            var regressionLine = new LineSeries()
            {
                Color = OxyColors.Red,
                StrokeThickness = 2,
                //MarkerSize = 3,
                //MarkerStroke = OxyColors.Red,
                //MarkerType = MarkerType.Diamond,
                Dashes = new double[] { 2, 2 }
            };
            plotModel.Series.Add(regressionLine);
            List<double> newY = new List<double>();
            //算法求a, b, c, d
            Func<double, double, double, double, double, double> modelFunc = (a, b, c, d, x) => (a - d) / (1 + Math.Pow((x / c), b)) + d;
            //将点（x，y）拟合到任意函数y的非线性最小二乘法
            var res = Fit.Curve(xData, yData, modelFunc, 1, 31, 1, 1, 1E-08,100000);

            //res.P0 = 1.099995;
            //res.P1 = 31.0305;
            //res.P2 = 3.072862;
            //res.P3 = 7.000825;

            //代入绘制拟合线
            Func<double, double> func = (x) => (res.P0 - res.P3) / (1 + Math.Pow((x / res.P2), res.P1)) + res.P3;
            plotModel.Series.Add(new FunctionSeries(func, xData[0] - 1, xData[xData.Length-1] + 1, 0.1, "Func(x)"));



            //res.P0 = 1.099995;
            //res.P1 = 31.0305;
            //res.P2 = 3.072862;
            //res.P3 = 7.000825;
            //for (int i = 0; i < xData.Length; i++)
            //{
            //    double y = (res.P0 - res.P3) / (1 + Math.Pow((xData[i] / res.P2), res.P1)) + res.P3;
            //    //regressionLine.Points.Add(new DataPoint(xData[i], y));
            //    newY.Add(y);
            //}



            ////拟合比例  R^2 
            //var result = CalculateRSquared(yData, newY.ToArray());

            #endregion

            #region 添加函数线
            //double a = 1.099995;
            //double b = 31.0305;
            //double c = 3.072862;
            //double d = 7.000825;
            //Func<double, double> modelFunc = (x) => (a - d) / (1 + Math.Pow((x / c), b)) + d;
            //plotModel.Series.Add(new FunctionSeries(modelFunc, 0, 50, 0.1, "sin(x)"));
            #endregion

            return plotModel;
        }

        /// <summary>
        /// 计算R^2,R^2这个值越接近1，说明拟合出来的曲线跟原曲线就越接近
        /// </summary>
        /// <param name="Y">实际的Y</param>
        /// <param name="Ytest">代入拟合曲线方程得到的Y</param>
        /// <returns>返回R^2</returns>
        public double CalculateRSquared(double[] Y, double[] Ytest)
        {
            //平方Person积-动量相关系数
            //计算r^2，即观测结果与观测预测值之间的样本相关系数的平方。不要与R^2混淆，即决定系数
            double RSquared = GoodnessOfFit.RSquared(Y, Ytest);
            return RSquared;
        }


        //曲线图
        public PlotModel Example1()
        {
            var tmp = new PlotModel() { Title = "十分士大夫" };
            tmp.Axes.Add(new LinearAxis() { MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, TickStyle = TickStyle.Outside, Position = AxisPosition.Left, Title = "ρ" });
            DateTime dt = new DateTime(2024, 1, 3);
            tmp.Axes.Add(new DateTimeAxis()//dt, dt.AddDays(1), AxisPosition.Bottom, null, null, DateTimeIntervalType.Hours
            {
                MajorGridlineStyle = LineStyle.Solid,
                Angle = 90,
                StringFormat = "HH:mm",
                MajorStep = 1.0 / 24 / 2, // 1/24 = 1 hour, 1/24/2 = 30 minutes
                IsZoomEnabled = true,
                MaximumPadding = 0,
                MinimumPadding = 0,
                Position = AxisPosition.Bottom,
                IntervalType = DateTimeIntervalType.Hours,
                TickStyle = TickStyle.None,
                Title = "日期：θ"
            });

            var ls = new LineSeries() { DataFieldX = "X", DataFieldY = "Y", Title = "Line1" };

            //model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, MinorTickSize = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid });
            //model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MinorTickSize = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid });


            List<Item> ii = new List<Item>();

            for (int i = 0; i < 24; i++)
                ii.Add(new Item { X = dt.AddHours(i), Y = i * i });
            ls.ItemsSource = ii;
            tmp.Series.Add(ls);
            return tmp;
        }

        public PlotModel Example2()
        {
            var plotModel = new PlotModel
            {
                Title = "Trigonometric functions",
                Subtitle = "Example using the FunctionSeries",
                PlotType = PlotType.Cartesian,
                Background = OxyColors.White
            };
            plotModel.Series.Add(new FunctionSeries(Math.Sin, -10, 10, 0.1, "sin(x)") { Color = OxyColors.Black });
            plotModel.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.1, "cos(x)") { Color = OxyColors.Green });
            plotModel.Series.Add(new FunctionSeries(t => 5 * Math.Cos(t), t => 5 * Math.Sin(t), 0, 2 * Math.PI, 0.1, "cos(t),sin(t)") { Color = OxyColors.Yellow });
            return plotModel;
        }

        //柱状图
        public PlotModel Example3()
        {
            // Create some data
            var datas = new Collection<Item1>
                            {
                                new Item1 {Label = "Apples", Value1 = 37, Value2 = 12, Value3 = 19},
                                new Item1 {Label = "Pears", Value1 = 7, Value2 = 21, Value3 = 9},
                                new Item1 {Label = "Bananas", Value1 = 23, Value2 = 2, Value3 = 29}
                            };

            // Create the plot model
            var tmp = new PlotModel() { LegendPlacement = LegendPlacement.Outside, LegendPosition = LegendPosition.RightTop, LegendOrientation = LegendOrientation.Vertical, Title = "Column series" };

            // Add the axes, note that MinimumPadding and AbsoluteMinimum should be set on the value axis.
            tmp.Axes.Add(new CategoryAxis { ItemsSource = datas, LabelField = "Label" });
            tmp.Axes.Add(new LinearAxis() { MinimumPadding = 0, AbsoluteMinimum = 0, Position = AxisPosition.Left });

            // Add the series, note that the the BarSeries are using the same ItemsSource as the CategoryAxis.
            tmp.Series.Add(new ColumnSeries { Title = "2009", ItemsSource = datas, ValueField = "Value1" });
            tmp.Series.Add(new ColumnSeries { Title = "2010", ItemsSource = datas, ValueField = "Value2" });
            tmp.Series.Add(new ColumnSeries { Title = "2011", ItemsSource = datas, ValueField = "Value3" });
            return tmp;
        }

        private static PlotModel Example4(AxisPosition position = AxisPosition.Bottom)
        {
            int n = 1000;
            var model = new PlotModel
            {
                Title = string.Format("ScatterSeries and RangeColorAxis (n={0})", n),
                Background = OxyColors.LightGray
            };

            model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom });
            model.Axes.Add(new LinearAxis { Position = AxisPosition.Left });

            var rca = new RangeColorAxis { Position = position, Maximum = 2, Minimum = -2 };
            rca.AddRange(0, 0.5, OxyColors.Blue);
            rca.AddRange(-0.2, -0.1, OxyColors.Red);
            model.Axes.Add(rca);

            var s1 = new ScatterSeries { MarkerType = MarkerType.Square, MarkerSize = 6, };

            var random = new Random(13);
            for (int i = 0; i < n; i++)
            {
                double x = (random.NextDouble() * 2.2) - 1.1;
                s1.Points.Add(new ScatterPoint(x, random.NextDouble()) { Value = x });
            }

            model.Series.Add(s1);
            return model;
        }

        //BarSeries
        public static PlotModel Example5()
        {
            var model = new PlotModel
            {
                Title = "BarSeries",
                LegendPlacement = LegendPlacement.Outside,
                LegendPosition = LegendPosition.BottomCenter,
                LegendOrientation = LegendOrientation.Horizontal,
                LegendBorderThickness = 0
            };

            var s1 = new BarSeries { Title = "Series 1", StrokeColor = OxyColors.Black, StrokeThickness = 1 };
            s1.Items.Add(new BarItem { Value = 25 });
            s1.Items.Add(new BarItem { Value = 137 });
            s1.Items.Add(new BarItem { Value = 18 });
            s1.Items.Add(new BarItem { Value = 40 });

            var s2 = new BarSeries { Title = "Series 2", StrokeColor = OxyColors.Black, StrokeThickness = 1 };
            s2.Items.Add(new BarItem { Value = 12 });
            s2.Items.Add(new BarItem { Value = 14 });
            s2.Items.Add(new BarItem { Value = 120 });
            s2.Items.Add(new BarItem { Value = 26 });

            var categoryAxis = new CategoryAxis { Position = AxisPosition.Left };
            categoryAxis.Labels.Add("Category A");
            categoryAxis.Labels.Add("Category B");
            categoryAxis.Labels.Add("Category C");
            categoryAxis.Labels.Add("Category D");
            var valueAxis = new LinearAxis { Position = AxisPosition.Bottom, MinimumPadding = 0, MaximumPadding = 0.06, AbsoluteMinimum = 0 };
            model.Series.Add(s1);
            model.Series.Add(s2);
            model.Axes.Add(categoryAxis);
            model.Axes.Add(valueAxis);
            return model;
        }

        //柱状图  Graph1
        public static PlotModel Example6()
        {
            var pm = new PlotModel { Title = "Q1 2003 Calls by Region", PlotAreaBorderThickness = new OxyThickness(0) };
            var categoryAxis = new CategoryAxis
            {
                AxislineStyle = LineStyle.Solid,
                TickStyle = TickStyle.None
            };
            categoryAxis.Labels.AddRange(new[] { "North", "East", "South", "West" });
            pm.Axes.Add(categoryAxis);
            pm.Axes.Add(
                new LinearAxis
                {
                    Position = AxisPosition.Left,
                    Minimum = 0,
                    Maximum = 6000,
                    MajorStep = 1000,
                    MinorStep = 1000,
                    AxislineStyle = LineStyle.Solid,
                    TickStyle = TickStyle.Outside,
                    StringFormat = "#,0"
                });
            var series = new ColumnSeries { FillColor = OxyColors.Black };
            series.Items.Add(new ColumnItem { Value = 3000 });
            series.Items.Add(new ColumnItem { Value = 4500 });
            series.Items.Add(new ColumnItem { Value = 2100 });
            series.Items.Add(new ColumnItem { Value = 4800 });
            pm.Series.Add(series);


            pm.SubtitleColor = OxyColors.LightGreen;
            pm.Subtitle = "Subtitle";
            pm.LegendTitle = "Легенда";
            pm.LegendOrientation = LegendOrientation.Horizontal;
            pm.LegendPlacement = LegendPlacement.Outside;
            pm.LegendPosition = LegendPosition.TopRight;
            pm.LegendBackground = OxyColor.FromAColor(200, OxyColors.White);
            pm.LegendBorder = OxyColors.Black;
            pm.MouseDown += (s, e) =>
            {
                try
                {
                    if (e.IsControlDown)
                    {
                        DataPoint date = (DataPoint)e.HitTestResult.Item;
                        MessageBox.Show($"{date.X}----{date.Y}");
                    }
                }
                catch (Exception)
                {

                }
            };
            return pm;
        }

        //饼状图
        public static PlotModel Example7()
        {
            var plotModel = new PlotModel { Title = "World population by continent" };
            var pieSeries = new PieSeries();
            pieSeries.Slices.Add(new PieSlice("Africa", 1030) { IsExploded = true });
            pieSeries.Slices.Add(new PieSlice("Americas", 929) { IsExploded = true });
            pieSeries.Slices.Add(new PieSlice("Asia", 4157));
            pieSeries.Slices.Add(new PieSlice("Europe", 739) { IsExploded = true });
            pieSeries.Slices.Add(new PieSlice("Oceania", 35) { IsExploded = true });
            pieSeries.InnerDiameter = 0.2;
            pieSeries.ExplodedDistance = 0;
            pieSeries.Stroke = OxyColors.Black;
            pieSeries.StrokeThickness = 1.0;
            pieSeries.AngleSpan = 360;
            pieSeries.StartAngle = 0;
            plotModel.Series.Add(pieSeries);
            return plotModel;
        }

        //实时折线图  TimeSpanaxisPlotModel
        public static PlotModel Example8()
        {
            var start = new TimeSpan(0, 0, 0, 0);
            var end = new TimeSpan(0, 24, 0, 0);
            double increment = 3600;

            // Create a random data collection
            var r = new Random(7);
            var data = new Collection<TimeValue>();
            var current = start;
            while (current <= end)
            {
                data.Add(new TimeValue { Time = current, Value = r.NextDouble() });
                current = current.Add(new TimeSpan(0, 0, (int)increment));
            }

            var plotModel1 = new PlotModel { Title = "TimeSpan axis" };
            var timeSpanAxis1 = new TimeSpanAxis { Position = AxisPosition.Bottom, StringFormat = "h:mm" };
            plotModel1.Axes.Add(timeSpanAxis1);
            var linearAxis1 = new LinearAxis { Position = AxisPosition.Left };
            plotModel1.Axes.Add(linearAxis1);
            var lineSeries1 = new LineSeries
            {
                Color = OxyColor.FromArgb(255, 78, 154, 6),
                MarkerFill = OxyColor.FromArgb(255, 78, 154, 6),
                MarkerStroke = OxyColors.ForestGreen,
                MarkerType = MarkerType.Plus,
                StrokeThickness = 1,
                DataFieldX = "Time",
                DataFieldY = "Value",
                ItemsSource = data
            };
            plotModel1.Series.Add(lineSeries1);
            return plotModel1;
        }
    }

    internal class TimeValue
    {
        public TimeSpan Time { get; set; }
        public double Value { get; set; }
    }

    public class Item1
    {
        public string Label { get; set; }
        public int Value1 { get; set; }
        public int Value2 { get; set; }
        public int Value3 { get; set; }

    }

    public class Item
    {
        public DateTime X { get; set; }
        public int Y { get; set; }
    }
    public class ItemNew
    {
        public double X { get; set; }
        public double Y { get; set; }
    }
}
